#ifndef ITERATORCORE_H_INCLUDED
#define ITERATORCORE_H_INCLUDED

namespace CEngine {

template<class E, class K>
Iterator<E,K>::Iterator(void)
    :   type(None)
{
    iteratorData = {0};
}



template<class E, class K>
E* Iterator<E,K>::operator[](K position) {
    switch(type) {
        case Vector:
            iteratorData.vector.currentElement = iteratorData.vector.basePointer->array + position;
            return iteratorData.vector.currentElement;
        case LinkedList:
            iteratorData.list.currentElement = &iteratorData.list.basePointer->traverseList(position, this);
            iteratorData.list.position = position % (iteratorData.list.basePointer->size + 1);
            return iteratorData.list.currentElement->element;
        case Deque:
            iteratorData.deque.position = position;
            dequeRandomAccess();
            return iteratorData.deque.currentElement;
        case Dictionary:
            return dictionaryRandomAccess(position);
        default:
            return NULL;
    }
}

template<class E, class K>
E* Iterator<E,K>::operator++(void) {
    switch(type) {
        case Vector:
            return ++iteratorData.vector.currentElement;
        case LinkedList:
            iteratorData.list.currentElement = iteratorData.list.currentElement->next;
            iteratorData.list.position = ++iteratorData.list.position % (iteratorData.list.basePointer->size + 1);
            return iteratorData.list.currentElement->element;
        case Deque:
            ++iteratorData.deque.position;
            ++iteratorData.deque.currentElement;
            if(iteratorData.deque.currentElement == iteratorData.deque.endElement) {
                ++iteratorData.deque.currentArray;
                iteratorData.deque.startElement = *iteratorData.deque.currentArray;
                iteratorData.deque.currentElement = iteratorData.deque.startElement;
                iteratorData.deque.endElement = iteratorData.deque.startElement + Constants::Containers::dequeBlockSize;
            }
            return iteratorData.deque.currentElement;
        case Dictionary:
            {
            if(iteratorData.dictionary.currentNode->right != iteratorData.dictionary.basePointer->nil) {
                iteratorData.dictionary.currentNode = iteratorData.dictionary.currentNode->right;
                while(iteratorData.dictionary.currentNode->left != iteratorData.dictionary.basePointer->nil) {
                    iteratorData.dictionary.currentNode = iteratorData.dictionary.currentNode->left;
                }
                iteratorData.dictionary.currentElement = &iteratorData.dictionary.currentNode->element;
                iteratorData.dictionary.currentKey = iteratorData.dictionary.currentNode->key;
                return iteratorData.dictionary.currentElement;
            }
            typename Dictionary<E,K>::Node* previous;
            do {
                previous = iteratorData.dictionary.currentNode;
                iteratorData.dictionary.currentNode = iteratorData.dictionary.currentNode->parent;
            } while(iteratorData.dictionary.currentNode->left != previous);
            iteratorData.dictionary.currentElement = &iteratorData.dictionary.currentNode->element;
            iteratorData.dictionary.currentKey = iteratorData.dictionary.currentNode->key;
            return iteratorData.dictionary.currentElement;
            }
        default:
            return NULL;
    }
}

template<class E, class K>
E* operator++(Iterator<E,K>& it, int) {
    E* temp = it;
    ++it;
    return temp;
}

template<class E, class K>
E* Iterator<E,K>::operator--(void) {
    switch(type) {
        case Vector:
            return --iteratorData.vector.currentElement;
        case LinkedList:
            iteratorData.list.currentElement = iteratorData.list.currentElement->previous;
            iteratorData.list.position = --iteratorData.list.position % (iteratorData.list.basePointer->size + 1);
            return iteratorData.list.currentElement->element;
        case Deque:
            --iteratorData.deque.position;
            --iteratorData.deque.currentElement;
            if(iteratorData.deque.currentElement == iteratorData.deque.startElement) {
                --iteratorData.deque.currentArray;
                iteratorData.deque.startElement = *iteratorData.deque.currentArray;
                iteratorData.deque.currentElement = iteratorData.deque.startElement;
                iteratorData.deque.endElement = iteratorData.deque.startElement + Constants::Containers::dequeBlockSize;
            }
            return iteratorData.deque.currentElement;
        case Dictionary:
            {
            if(iteratorData.dictionary.currentNode->left != iteratorData.dictionary.basePointer->nil) {
                iteratorData.dictionary.currentNode = iteratorData.dictionary.currentNode->left;
                while(iteratorData.dictionary.currentNode->right != iteratorData.dictionary.basePointer->nil) {
                    iteratorData.dictionary.currentNode = iteratorData.dictionary.currentNode->right;
                }
                iteratorData.dictionary.currentElement = &iteratorData.dictionary.currentNode->element;
                iteratorData.dictionary.currentKey = iteratorData.dictionary.currentNode->key;
                return iteratorData.dictionary.currentElement;
            }
            typename Dictionary<E,K>::Node* previous = iteratorData.dictionary.currentNode;
            do {
                iteratorData.dictionary.currentNode = iteratorData.dictionary.currentNode->parent;
                previous = iteratorData.dictionary.currentNode;
            } while(iteratorData.dictionary.currentNode->right != previous);
            iteratorData.dictionary.currentElement = &iteratorData.dictionary.currentNode->element;
            iteratorData.dictionary.currentKey = iteratorData.dictionary.currentNode->key;
            return iteratorData.dictionary.currentElement;
            }
        default:
            return NULL;
    }
}

template<class E, class K>
E* operator--(Iterator<E,K>& it, int) {
    E* temp = it;
    --it;
    return temp;
}

template<class E, class K>
Iterator<E,K>::operator E*(void) const {
    switch(type) {
        case Vector:
            return iteratorData.vector.currentElement;
        case LinkedList:
            return iteratorData.list.currentElement->element;
        case Deque:
            return iteratorData.deque.currentElement;
        case Dictionary:
            return iteratorData.dictionary.currentElement;
        default:
            return NULL;
    }
}

template<class E, class K>
bool Iterator<E,K>::operator==(const Iterator<E,K>& it) const {
    ASSERT(type == it.type);

    void* base1 = (void*)((U64)(this) + 8);
    void* base2 = (void*)((U64)(&it) + 8);
    ASSERT(base1 == base2);

    switch(type) {
        case Vector:
            if(iteratorData.vector == it.iteratorData.vector) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list == it.iteratorData.list) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque == it.iteratorData.deque) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentNode == it.iteratorData.dictionary.currentNode) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
bool Iterator<E,K>::operator!=(const Iterator<E,K>& it) const {
    ASSERT(type == it.type);

    void* base1 = (void*)((U64)(this) + 8);
    void* base2 = (void*)((U64)(&it) + 8);
    ASSERT(base1 == base2);

    switch(type) {
        case Vector:
            if(iteratorData.vector != it.iteratorData.vector) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list != it.iteratorData.list) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque != it.iteratorData.deque) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentNode != it.iteratorData.dictionary.currentNode) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
bool Iterator<E,K>::operator>(const Iterator<E,K>& it) const {
    ASSERT(type == it.type);

    void* base1 = (void*)((U64)(this) + 8);
    void* base2 = (void*)((U64)(&it) + 8);
    ASSERT(base1 == base2);

    switch(type) {
        case Vector:
            if(iteratorData.vector.currentElement > it.iteratorData.vector.currentElement) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list.position > it.iteratorData.list.position) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque.position > it.iteratorData.deque.position) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentKey > it.iteratorData.dictionary.currentKey) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
bool Iterator<E,K>::operator<(const Iterator<E,K>& it) const {
    ASSERT(type == it.type);

    U64 base1 = *(U64*)((U64)(this) + 8);
    U64 base2 = *(U64*)((U64)(&it) + 8);
    ASSERT(base1 == base2);

    switch(type) {
        case Vector:
            if(iteratorData.vector.currentElement < it.iteratorData.vector.currentElement) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list.position < it.iteratorData.list.position) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque.position < it.iteratorData.deque.position) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentKey < it.iteratorData.dictionary.currentKey) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
bool Iterator<E,K>::operator>=(const Iterator<E,K>& it) const {
    ASSERT(type == it.type);

    void* base1 = (void*)((U64)(this) + 8);
    void* base2 = (void*)((U64)(&it) + 8);
    ASSERT(base1 == base2);

    switch(type) {
        case Vector:
            if(iteratorData.vector.currentElement >= it.iteratorData.vector.currentElement) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list.position >= it.iteratorData.list.position) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque.position >= it.iteratorData.deque.position) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentKey >= it.iteratorData.dictionary.currentKey) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
bool Iterator<E,K>::operator<=(const Iterator<E,K>& it) const {
    ASSERT(type == it.type);

    void* base1 = (void*)((U64)(this) + 8);
    void* base2 = (void*)((U64)(&it) + 8);
    ASSERT(base1 == base2);

    switch(type) {
        case Vector:
            if(iteratorData.vector.currentElement <= it.iteratorData.vector.currentElement) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list.position <= it.iteratorData.list.position) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque.position <= it.iteratorData.deque.position) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentKey <= it.iteratorData.dictionary.currentKey) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
bool Iterator<E,K>::operator==(const I64 n) const {
    switch(type) {
        case Vector:
            if(iteratorData.vector.currentElement == (iteratorData.vector.basePointer + n)) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list.position == n) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque.position == n) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentKey == n) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
bool Iterator<E,K>::operator!=(const I64 n) const {
    switch(type) {
        case Vector:
            if(iteratorData.vector.currentElement != (iteratorData.vector.basePointer + n)) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list.position != n) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque.position != n) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentKey != n) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
bool Iterator<E,K>::operator>(const I64 n) const {
    switch(type) {
        case Vector:
            if(iteratorData.vector.currentElement > (iteratorData.vector.basePointer + n)) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list.position > n) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque.position > n) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentKey > n) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
bool Iterator<E,K>::operator<(const I64 n) const {
    switch(type) {
        case Vector:
            if(iteratorData.vector.currentElement < (iteratorData.vector.basePointer + n)) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list.position < n) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque.position < n) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentKey < n) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
bool Iterator<E,K>::operator>=(const I64 n) const {
    switch(type) {
        case Vector:
            if(iteratorData.vector.currentElement >= (iteratorData.vector.basePointer + n)) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list.position >= n) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque.position >= n) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentKey >= n) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
bool Iterator<E,K>::operator<=(const I64 n) const {
    switch(type) {
        case Vector:
            if(iteratorData.vector.currentElement <= (iteratorData.vector.basePointer + n)) {
                return true;
            }
            break;
        case LinkedList:
            if(iteratorData.list.position <= n) {
                return true;
            }
            break;
        case Deque:
            if(iteratorData.deque.position <= n) {
                return true;
            }
            break;
        case Dictionary:
            if(iteratorData.dictionary.currentKey <= n) {
                return true;
            }
            break;
    }
    return false;
}

template<class E, class K>
E* Iterator<E,K>::operator+(const I64 n) const {
    switch(type) {
        case Vector:
            return iteratorData.vector.currentElement + n;
        case LinkedList:
            return iteratorData.list.basePointer->traverseList(iteratorData.list.position + n, const_cast<Iterator<E,K>*>(this)).element;
        case Deque:
            return &iteratorData.deque.basePointer->getElement(iteratorData.deque.position + n);
        case Dictionary:
            return &iteratorData.dictionary.basePointer->search(iteratorData.dictionary.currentKey + n)->element;
        default:
            return NULL;
    }
}

template<class E, class K>
E* operator+(const I64 n, Iterator<E,K>& it) {
    return it + n;
}

template<class E, class K>
E* Iterator<E,K>::operator-(const I64 n) const {
    switch(type) {
        case Vector:
            return iteratorData.vector.currentElement - n;
        case LinkedList:
            return &iteratorData.list.basePointer->traverseList(iteratorData.list.position - n, const_cast<Iterator<E,K>*>(this)).element;
        case Deque:
            return &iteratorData.deque.basePointer->getElement(iteratorData.deque.position - n);
        case Dictionary:
            return iteratorData.dictionary.basePointer->search(iteratorData.dictionary.currentKey - n)->element;
        default: return NULL;
    }
}

template<class E, class K>
E* Iterator<E,K>::operator-(const Iterator<E,K>& it) const {
    ASSERT(type == it.type);

    void* base1 = (void*)((U64)(this) + 8);
    void* base2 = (void*)((U64)(&it) + 8);
    ASSERT(base1 == base2);

    switch(type) {
        case Vector:
            I64 offset1 = iteratorData.vector.currentElement - iteratorData.vector.basePointer;
            I64 offset2 = it.iteratorData.vector.currentElement - it.iteratorData.vector.basePointer;
            return iteratorData.vector.basePointer + (offset1 - offset2);
        case LinkedList:
            return &iteratorData.list.basePointer->traverseList(iteratorData.list.position - it.iteratorData.list.position).element;
        case Deque:
            return &iteratorData.deque.basePointer->getElement(iteratorData.deque.position - it.iteratorData.deque.position);
        case Dictionary:
            return iteratorData.dictionary.basePointer->search(iteratorData.dictionary.currentKey + it.iteratorData.dictionary.currentKey)->element;
        default:
            return NULL;
    }
}

template<class E, class K>
Iterator<E,K>& Iterator<E,K>::operator+=(const I64 n) {
    switch(type) {
        case Vector:
            iteratorData.vector.currentElement += n;
            break;
        case LinkedList:
            iteratorData.list.position = (iteratorData.list.position + n) % (iteratorData.list.basePointer->size);
            iteratorData.list.currentElement = iteratorData.list.basePointer->traverseList(iteratorData.list.position, const_cast<Iterator<E,K>*>(this)).element;
        case Deque:
            iteratorData.deque.position += n;
            iteratorData.deque.currentElement += n;
            if(iteratorData.deque.currentElement >= iteratorData.deque.endElement) {
                iteratorData.deque.currentArray = (iteratorData.deque.basePointer->start + iteratorData.deque.position) / Constants::Containers::dequeBlockSize;
                iteratorData.deque.startElement = *iteratorData.deque.currentArray;
                iteratorData.deque.currentElement = iteratorData.deque.startElement[(iteratorData.deque.basePointer->start + iteratorData.deque.position) % Constants::Containers::dequeBlockSize];
                iteratorData.deque.endElement = iteratorData.deque.startElement + Constants::Containers::dequeBlockSize;
            }
            return iteratorData.deque.currentElement;
        case Dictionary:
            *this[iteratorData.dictionary.currentKey += n];
        default: break;
    }
    return this;
}

template<class E, class K>
Iterator<E,K>& Iterator<E,K>::operator-=(const I64 n) {
    switch(type) {
        case Vector:
            iteratorData.vector.currentElement -= n;
            break;
        case LinkedList:
            iteratorData.list.position = (iteratorData.list.position - n) % (iteratorData.list.basePointer->size);
            iteratorData.list.currentElement = iteratorData.list.basePointer->traverseList(iteratorData.list.position, const_cast<Iterator<E,K>*>(this)).element;
        case Deque:
            iteratorData.deque.position -= n;
            iteratorData.deque.currentElement -= n;
            if(iteratorData.deque.currentElement < iteratorData.deque.startElement) {
                iteratorData.deque.currentArray = (iteratorData.deque.basePointer->start + iteratorData.deque.position) / Constants::Containers::dequeBlockSize;
                iteratorData.deque.startElement = *iteratorData.deque.currentArray;
                iteratorData.deque.currentElement = iteratorData.deque.startElement[(iteratorData.deque.basePointer->start + iteratorData.deque.position) % Constants::Containers::dequeBlockSize];
                iteratorData.deque.endElement = iteratorData.deque.startElement + Constants::Containers::dequeBlockSize;
            }
            return iteratorData.deque.currentElement;
        case Dictionary:
            *this[iteratorData.dictionary.currentKey -= n];
        default: break;
    }
    return this;
}

template<class E, class K>
K Iterator<E,K>::getPosition(void) const {
    switch(type) {
        case Vector:
            return (K)(iteratorData.vector.currentElement - iteratorData.vector.basePointer->array);
        case LinkedList:
            return iteratorData.list.position;
        case Deque:
            return iteratorData.deque.position;
        case Dictionary:
            return iteratorData.dictionary.currentKey;
        default: return K();
    }
}

template<class E, class K>
K Iterator<E,K>::getKey(void) const {
    switch(type) {
        case Vector:
            return (K)(iteratorData.vector.currentElement - iteratorData.vector.basePointer->array);
        case LinkedList:
            return iteratorData.list.position;
        case Deque:
            return iteratorData.deque.position;
        case Dictionary:
            return iteratorData.dictionary.currentKey;
        default: return K();
    }
}

}

#endif // ITERATORCORE_H_INCLUDED
